package org.madmatt.tdd
{
    import flash.text.TextField;
    
    import org.flexunit.reporting.FailureFormatter;
    import org.flexunit.runner.IDescription;
    import org.flexunit.runner.Result;
    import org.flexunit.runner.notification.Failure;
    import org.flexunit.runner.notification.RunListener;

    /**
     * Simple flexunit results listener.
     */
    public class LogListener extends RunListener
    {
		public var nbTests:int;
        public var nbSuccess:int;
        public var nbFailures:int;
		private var textField:TextField;

        public function LogListener(textField:TextField):void
        {
			this.textField = textField;
        }
		
		protected function log(msg:String):void{
			trace(msg);
			this.textField.appendText(msg + '\n');
			this.textField.scrollV = this.textField.maxScrollV;
		}

		/**
		 * @inheritDoc
		 */
		override public function testRunFinished( result:Result ):void {
			nbSuccess = nbTests - nbFailures;
			printHeader( result.runTime );
			printFailures( result );
			printFooter( result );
		}
		
		/**
		 * @inheritDoc
		 */
		override public function testStarted( description:IDescription ):void {
			nbTests++;
			log( description.displayName + " ." );
		}
		
		/**
		 * @inheritDoc
		 */
		override public function testFailure( failure:Failure ):void {
			nbFailures++;
			//Determine if the exception in the failure is considered an error
			if ( FailureFormatter.isError( failure.exception ) ) {
				log( failure.description.displayName + " E" );
			} else {
				log( failure.description.displayName + " F" );
			}
		}
		
		/**
		 * @inheritDoc
		 */
		override public function testIgnored( description:IDescription ):void {
			log( description.displayName + " I" );
		}
		
		public function testTimed( description:IDescription, runTime:Number ):void {
			log( description.displayName + " took " + runTime + " ms " );
		}
		
		/*
		* Internal methods
		*/
		
		/**
		 * logs a header that provides the total run time
		 * 
		 * @param runTime The total run time of all tests in milliseconds
		 */
		protected function printHeader( runTime:Number ):void {
			log( "Time: " + elapsedTimeAsString(runTime) );
			//log( elapsedTimeAsString(runTime) );
		}
		
		/**
		 * logs all failures that were received in the result
		 * 
		 * @param result The result that contains potential failures
		 */
		protected function printFailures( result:Result ):void {
			var failures:Array = result.failures;
			//Determine if there are any failures to print
			if (failures.length == 0)
				return;
			if (failures.length == 1)
				log( "There was " + failures.length + " failure:" );
			else
				log("There were " + failures.length + " failures:" );
			
			//Print each failure
			for ( var i:int=0; i<failures.length; i++ ) {
				printFailure( failures[ i ], String( i+1 ) );
			}
		}
		
		/**
		 * logs a provided failure with a certain prefix
		 * 
		 * @param failure The provided failure
		 * @param prefix A String prefix for the failure
		 */
		protected function printFailure( failure:Failure, prefix:String ):void {
			//log( prefix + " " + failure.testHeader + " " + failure.stackTrace);
			log( prefix + " " + failure.testHeader );
		}
		
		/**
		 * logs a footer for the provided result
		 * 
		 * @param result The result that contains the total run count
		 */
		protected function printFooter( result:Result ):void {
			//Determine if the result was a success
			if (result.successful ) {
				log( "OK (" + result.runCount + " test " + (result.runCount == 1 ? "" : "s") + ")" );
			} else {
				log( "FAILURES!!! Tests run: " + result.runCount + ", " + result.failureCount + " Failures:" );
			}
		}
		
		/**
		 * Returns the formatted string of the elapsed time. Duplicated from
		 * BaseTestRunner. Fix it.
		 */
		protected function elapsedTimeAsString( runTime:Number ):String {
			return String( runTime / 1000 );
		}
		
		public function resetCounters():void
		{
			nbFailures = 0;
			nbSuccess = 0;
			nbTests = 0;
		}
	}
}
